package main.objects;

import main.FactageSmet;
import main.MainFrame;
import org.jdom2.Attribute;
import org.jdom2.Document;
import org.jdom2.Element;
import org.jdom2.JDOMException;
import org.jdom2.input.SAXBuilder;

import javax.swing.*;
import java.io.File;
import java.io.IOException;
import java.math.BigDecimal;
import java.util.*;

public class XMLSmet {

    private String smetName;
    private Element rootNode;
    private SmetNode newSmet, curSmr;

    private RegionInfo regionInfo = new RegionInfo();
    private NormsNRSP norms;
    private Parameters params;
    private Koefficients globalCoeffs;
    private BigDecimal regionalK;


    public XMLSmet(File file) throws JDOMException, IOException {

        smetName = file.getName().replace(".xml", "");

        SAXBuilder builder = new SAXBuilder();
        Document xmlSmet = builder.build(file);
        rootNode = xmlSmet.getRootElement();

        setHeader();
        setNRSPCalc();
        setGlobalCoeffs();
        getStructure();

    }

    //region Определение титула сметы и параметров применения НР и СП
    private void setHeader() {

        if (rootNode.getChild("DbInfo")!= null) {

            Element smetHeader = rootNode.getChild("DbInfo").getChild("Document");
            newSmet = new SmetNode(
                    smetHeader.getAttributeValue("Caption"),
                    smetHeader.getAttributeValue("LocalNumber") != null ? smetHeader.getAttributeValue("LocalNumber") : smetHeader.getAttributeValue("Caption"),
                    new BigDecimal("0"),
                    "");

        } else {
            newSmet = new SmetNode(smetName, "", new BigDecimal(0), "");
        }

        newSmet.setPz(new BigDecimal(0));

        newSmet.createSmet();

        setRegion();
        setParameters();

    }
    //endregion

    private void setRegion() {

        if (rootNode.getChild("RegionInfo") == null) return;

        Element region = rootNode.getChild("RegionInfo");

        regionInfo.setRegionName(region.getAttributeValue("RegionName"));
        regionInfo.setRegionId(Integer.valueOf(region.getAttributeValue("RegionID")));
        regionInfo.setZone84Name(region.getAttributeValue("Zone84Name "));
        regionInfo.setZone84Id(Integer.valueOf(region.getAttributeValue("Zone84ID")));
        regionInfo.setZone01Name(region.getAttributeValue("Zone01Name"));
        regionInfo.setZone01Id(Integer.valueOf(region.getAttributeValue("Zone01ID")));

    }

    private void setParameters() {

        if (rootNode.getChild("Parameters") == null) return;

        Element param = rootNode.getChild("Parameters");

        params = new Parameters();
        params.setBasePrices(param.getAttributeValue("BasePrices"));
        params.setBaseCalcVrs(param.getAttributeValue("BaseCalcVrs"));

        Attribute attrOptions = param.getAttribute("Options");

        params.setRoundSum(attrOptions.getValue().contains("RoundPos"));
        params.setAddZatr(attrOptions.getValue().contains("AddZatr"));
        params.setGlobalNR(attrOptions.getValue().contains("CommonNakl"));
        params.setGlobalSP(attrOptions.getValue().contains("CommonPlan"));
        params.setTzDigits(Integer.valueOf(param.getAttributeValue("TzDigits")));

    }

    //region Определение расчетов НР и СП, а также коэффициентов
    private void setNRSPCalc() {

        norms = new NormsNRSP();

        if (params.getGlobalNR()) {

            //region Расчет НР по общему нормативу
            norms.setGlobalNorm(
                    new Norm(
                            "Общий норматив",
                            new BigDecimal(rootNode.getChild("Parameters").getChild("CommonNPCurr").getAttributeValue("Nacl").replace(",", ".")),
                            rootNode.getChild("Parameters").getChild("CommonNPCurr").getAttributeValue("NaclMask").replace("ФОТ", "OZ,ZM").replace("ПЗ", "PZ").replace("ОЗП", "OZ").replace("ЭМ", "EM").replace("ЗПМ", "ZM").replace("МАТ", "MT")));

            //endregion

        } else {

            //region Расчет НР и СП по нормативам видов работ
            if (rootNode.getChild("VidRab_Catalog") != null &&
                rootNode.getChild("VidRab_Catalog").getChild("Vids_Rab") != null &&
                rootNode.getChild("VidRab_Catalog").getChild("Vids_Rab").getChildren().size() > 0
            ) {

                norms.setNorms(new HashMap<String, Norm>());

                for (Element vidRabGroup : rootNode.getChild("VidRab_Catalog").getChild("Vids_Rab").getChildren()) {
                    if (!vidRabGroup.getName().equals("VidRab_Group")) continue;

                    for (Element vidRab : vidRabGroup.getChildren()) {

                        if (vidRab.getAttribute("Nacl") == null) continue;

                        Norm newNorm = new Norm(
                                vidRab.getAttributeValue("Caption"),
                                new BigDecimal(vidRab.getAttributeValue("Nacl").replace(",", ".")),
                                vidRab.getAttributeValue("NaclMask").replace("ФОТ", "OZ").replace("ФОТ", "OZ,ZM").replace("ПЗ", "PZ").replace("ОЗП", "OZ").replace("ЭМ", "EM").replace("ЗПМ", "ZM").replace("МАТ", "MT")
                                );

                        if (vidRab.getAttribute("Plan") != null && !params.getGlobalSP()) {

                            newNorm.setSp(new BigDecimal(vidRab.getAttributeValue("Plan").replace(",", ".")));
                            newNorm.setSpMask(vidRab.getAttributeValue("PlanMask").replace("ФОТ", "OZ,ZM").replace("ПЗ", "PZ").replace("ОЗП", "OZ").replace("ЭМ", "EM").replace("ЗПМ", "ZM").replace("МАТ", "MT"));

                        }

                        norms.getNorms().put(newNorm.getCaption(), newNorm);

                    }

                }

                if (!params.getGlobalSP()) return;

            } else {
                JOptionPane.showMessageDialog(FactageSmet.mF, "Для сметы:" + newSmet.getName() + "\nНе определен справочник работ для применения НР и СП.", "Ошибка", JOptionPane.ERROR_MESSAGE);
            }
            //endregion

        }

        if (params.getGlobalSP()) {

            //region Расчет СП по общему нормативу
            if (norms.getGlobalNorm() == null) norms.setGlobalNorm(new Norm("Общий норматив"));

            norms.getGlobalNorm().setSp(new BigDecimal(rootNode.getChild("Parameters").getChild("CommonNPCurr").getAttributeValue("Plan").replace(",", ".")));
            norms.getGlobalNorm().setSpMask(rootNode.getChild("Parameters").getChild("CommonNPCurr").getAttributeValue("PlanMask").replace("ФОТ", "OZ,ZM").replace("ПЗ", "PZ").replace("ОЗП", "OZ").replace("ЭМ", "EM").replace("ЗПМ", "ZM").replace("МАТ", "MT"));

            //endregion

        } else {

            //region Расчет СП по нормативам видов работ
            if (rootNode.getChild("VidRab_Catalog") != null &&
                    rootNode.getChild("VidRab_Catalog").getChild("Vids_Rab") != null &&
                    rootNode.getChild("VidRab_Catalog").getChild("Vids_Rab").getChildren().size() > 0
                    ) {

                if (norms.getNorms() == null) norms.setNorms(new HashMap<String, Norm>());

                for (Element vidRabGroup : rootNode.getChild("VidRab_Catalog").getChild("Vids_Rab").getChildren()) {
                    if (!vidRabGroup.getName().equals("VidRab_Group")) continue;

                    for (Element vidRab : vidRabGroup.getChildren()) {

                        if (vidRab.getAttribute("Plan") == null) continue;

                        String caption = vidRab.getAttributeValue("Caption");
                        Norm newNorm;

                        if (norms.getNorms().containsKey(caption)) {

                            newNorm = norms.getNorms().get(caption);

                        } else {

                            newNorm = new Norm(caption);

                        }

                        newNorm.setSp(new BigDecimal(vidRab.getAttributeValue("Plan").replace(",", ".")));
                        newNorm.setSpMask(vidRab.getAttributeValue("PlanMask").replace("ФОТ", "OZ,ZM").replace("ПЗ", "PZ").replace("ОЗП", "OZ").replace("ЭМ", "EM").replace("ЗПМ", "ZM").replace("МАТ", "MT"));

                        norms.getNorms().put(newNorm.getCaption(), newNorm);

                    }

                }

            } else {
                JOptionPane.showMessageDialog(FactageSmet.mF, "Для сметы:" + newSmet.getName() + "\nНе определен справочник работ для применения НР и СП.", "Ошибка", JOptionPane.ERROR_MESSAGE);
            }
            //endregion

        }

    }
    //endregion

    private void setGlobalCoeffs() {

        if (rootNode.getChild("RegionalK") != null && rootNode.getChild("RegionalK").getAttribute("Value") != null ) regionalK = new BigDecimal(rootNode.getChild("RegionalK").getAttributeValue("Value").replace(",", "."));
        else regionalK = new BigDecimal("1");

        globalCoeffs = parseKoefficients(rootNode.getChild("Koefficients"));

    }

    Koefficients parseKoefficients(Element koeffsElement) {

        if (koeffsElement == null || koeffsElement.getChildren().isEmpty()) return null;

        Koefficients coeffs = new Koefficients();

        for (Element k : koeffsElement.getChildren()) {

            int lev = 0;
            if (k.getAttribute("Level") != null) lev = Integer.valueOf(k.getAttributeValue("Level"));
            if (!coeffs.getLevels().contains(lev)) coeffs.getLevels().add(lev);

            ArrayList<Koeff> ks;
            if (!coeffs.getKoeffs().containsKey(lev)) {
                ks = new ArrayList<>();
                coeffs.getKoeffs().put(lev, ks);
            } else {
                ks = coeffs.getKoeffs().get(lev);
            }

            Koeff koeff = new Koeff();

            if (k.getAttribute("AllVidRabs") != null && k.getAttributeValue("AllVidRabs").equals("No") && k.getAttribute("VrsLinks") != null) {

                koeff.setAllVidRabs(false);
                String[] links = k.getAttributeValue("VrsLinks").replace("(", "").replace(")", "").replace(" ", "").split(",");
                for (String link : links) {
                    koeff.getVrsLink().add(link);
                }

            }

            if (k.getAttribute("AllChapters") != null && k.getAttributeValue("AllChapters").equals("No") && k.getAttribute("ChaptersLinks") != null) {

                koeff.setAllChapters(false);
                String[] links = k.getAttributeValue("ChaptersLinks").replace("(", "").replace(")", "").replace(" ", "").split(",");
                for (String link : links) {
                    koeff.getChaptersLink().add(link);
                }

            }

            if (k.getAttribute("Value_OZ") != null)
                koeff.getValues().put("OZ", new BigDecimal(k.getAttributeValue("Value_OZ").replace(",",".")));

            if (k.getAttribute("Value_EM") != null)
                koeff.getValues().put("EM", new BigDecimal(k.getAttributeValue("Value_EM").replace(",",".")));

            if (k.getAttribute("Value_MT") != null)
                koeff.getValues().put("MT", new BigDecimal(k.getAttributeValue("Value_MT").replace(",",".")));

            if (k.getAttribute("Value_PZ") != null)
                koeff.getValues().put("PZ", new BigDecimal(k.getAttributeValue("Value_PZ").replace(",",".")));

            if (k.getAttribute("Value_ZM") != null)
                koeff.getValues().put("ZM", new BigDecimal(k.getAttributeValue("Value_ZM").replace(",",".")));

            ks.add(koeff);

        }

        coeffs.sortLevels();

        return coeffs;
    }

    private void getStructure() {

        if (rootNode.getChild("Chapters") == null ||
            rootNode.getChild("Chapters").getChildren().size() < 1) {

            JOptionPane.showMessageDialog(FactageSmet.mF, "Для сметы:" + newSmet.getName() + "\nНе определена структура расценок.", "Ошибка", JOptionPane.ERROR_MESSAGE);
            return;
        }

        for (Element chapterElement: rootNode.getChild("Chapters").getChildren()) {

            SmetNode chapter = new SmetNode (chapterElement.getAttributeValue("Caption"), Integer.valueOf(chapterElement.getAttributeValue("SysID")), newSmet.getId());

            chapter.createChapter();
            newSmet.add(chapter);

            SmetNode header = null;
            SmetNode parent = chapter;

            for (Element child : chapterElement.getChildren()) {

                if (child.getName().equals("Header")) {

                    header = new SmetNode(newSmet.getId(), child.getAttributeValue("Caption"), chapter.getId());
                    header.createHeader();
                    chapter.add(header);
                    parent = header;

                } else if (child.getName().equals("Position")) {

                    HashMap<String, BigDecimal> smrValues = calcSmrValues(child);

                    SmetNode smr = new SmetNode(
                        Integer.valueOf(child.getAttributeValue("Number")),
                        child.getAttributeValue("Code"),
                        child.getAttributeValue("Caption"),

                        child.getAttributeValue("Units"),
                        smrValues.get("volume"),

                        smrValues.get("TZR"),
                        smrValues.get("OZ"),
                        smrValues.get("TZM"),
                        smrValues.get("EM"),
                        smrValues.get("ZM"),
                            smrValues.get("0"),
                        smrValues.get("PZ"),
                            smrValues.get("0"),
                            smrValues.get("0"),
                            smrValues.get("0"),

                        header != null ? header.getId() : 0,
                        chapter.getId(),
                        newSmet.getId()

                    );

                    smr.createSmr();
                    parent.add(smr);

                }
            }
        }

    }

    //region Расчет и парсинг показателей расценки
    private HashMap<String, BigDecimal> calcSmrValues(Element smr) {

        getResources(smr);

        Koefficients localK = parseKoefficients(smr.getChild("Koefficients"));

        HashMap<String, BigDecimal> smrValues = new HashMap<>();

        //region Количество по сметной строке
        smrValues.put("volume", new BigDecimal(smr.getChild("Quantity").getAttributeValue("Result").replace(",", ".")));
        //endregion

        //region Трудозатраты и ЗП рабочих
        if (smr.getChild("Resources")!= null && smr.getChild("Resources").getChild("Tzr") != null) {
            smrValues.put("TZR", new BigDecimal(smr.getChild("Resources").getChild("Tzr").getAttributeValue("Quantity").replace(",", ".")).multiply(smrValues.get("volume")));
        } else {
            smrValues.put("TZR", new BigDecimal(0));
        }

        smrValues.put("OZ", new BigDecimal(smr.getChild("PriceBase") != null && smr.getChild("PriceBase").getAttribute("OZ") != null ? smr.getChild("PriceBase").getAttributeValue("OZ").replace(",", ".") : "0").multiply(smrValues.get("volume")));
        //endregion

        //region Затраты труда машинистов
        if (smr.getChild("Resources") != null && smr.getChild("Resources").getChild("Tzm") != null) {
            smrValues.put("TZM", new BigDecimal(smr.getChild("Resources").getChild("Tzm").getAttributeValue("Quantity").replace(",", ".")).multiply(smrValues.get("volume")));
        } else {
            smrValues.put("TZM", new BigDecimal(0));
        }

        smrValues.put("ZM", new BigDecimal(smr.getChild("PriceBase") != null && smr.getChild("PriceBase").getAttribute("ZM") != null ? smr.getChild("PriceBase").getAttributeValue("ZM").replace(",", ".") : "0").multiply(smrValues.get("volume")));

        smrValues.put("EM", new BigDecimal(smr.getChild("PriceBase") != null && smr.getChild("PriceBase").getAttribute("EM") != null ? smr.getChild("PriceBase").getAttributeValue("EM").replace(",", ".") : "0").multiply(smrValues.get("volume")));

        smrValues.put("EMR", (smrValues.containsKey("EM") ? smrValues.get("EM") : new BigDecimal(0)).subtract(smrValues.containsKey("ZM") ? smrValues.get("ZM") : new BigDecimal(0)));

        //endregion

        //region Стоимость материалов
        smrValues.put("MT", new BigDecimal(smr.getChild("PriceBase") != null && smr.getChild("PriceBase").getAttribute("MT") != null ? smr.getChild("PriceBase").getAttributeValue("MT").replace(",", ".") : "0").multiply(smrValues.get("volume")));
        //endregion

        assignKoefficients(localK, smrValues, smr);

        BigDecimal em = smrValues.containsKey("EM") ? smrValues.get("EM") : new BigDecimal("0");
        BigDecimal oz = smrValues.containsKey("OZ") ? smrValues.get("OZ") : new BigDecimal("0");
        BigDecimal mt = smrValues.containsKey("MT") ? smrValues.get("MT") : new BigDecimal("0");

        smrValues.put("PZ",em.add(oz).add(mt));

        if (params.getRoundSum()) roundValues(smrValues);

        smrValues.put("0", new BigDecimal("0"));

        return smrValues;

    }
    //endregion

    private void getResources(Element smr) {

        if (smr.getChild("Resources") == null || smr.getChild("Resources").getChildren().isEmpty()) return;

        Element rsrcs = smr.getChild("Resources");

        for (Element rsrcElement : rsrcs.getChildren() ) {

            if (rsrcElement.getName().equals("Tzr")) {

                SmetResource rsrc;
                if (MainFrame.CURRENT_TITUL.getTitulResources().containsKey("T")) {
                    rsrc = MainFrame.CURRENT_TITUL.getTitulResources().get("T");
                } else {
                    rsrc = new SmetResource("Затраты труда рабочих", "T", "T", "чел.час", MainFrame.CURRENT_TITUL.getId());
                    rsrc.create();
                }

                SmetResourceAssignment smetRa = new SmetResourceAssignment(rsrc, );


            } else if (rsrcElement.getName().equals("Tzm")) {



            } else if (rsrcElement.getName().equals("Mch")) {



            } else if (rsrcElement.getName().equals("Mat")) {



            }


        }

    }

    private void roundValues(HashMap<String, BigDecimal> smrValues) {

        for (String key : smrValues.keySet()) {
            if (key.equals("TZR") || key.equals("TZM") || key.equals("volume") ) continue;
            smrValues.put(key, smrValues.get(key).setScale(0, BigDecimal.ROUND_HALF_UP));
        }

    }

    private void assignKoefficients(Koefficients koefficients, HashMap<String, BigDecimal> smrValues, Element smr) {

        if (koefficients == null) return;

        for (int lev : koefficients.getLevels()) {

            HashMap<String, BigDecimal> levSum = new HashMap<>();
            for (Koeff koeff : koefficients.getKoeffs().get(lev)) {

                //region Расчет глобальных или коэффициентов по расценкам
                if (koeff.getAllChapters() && koeff.getAllVidRabs()) {

                    for (String key : koeff.getValues().keySet()) {

                        if (!smrValues.containsKey(key) && !key.equals("PZ") && !key.equals("EM")) {
                            System.out.println("smrow: " + smr.getAttributeValue("Number") + ", has no " + key );
                        } else {

                            if (key.equals("PZ")) {

                                List<String> em_keys = Arrays.asList("ZM", "EMR", "OZ", "MT");
                                for (String em_key : em_keys) {
                                    if (!smrValues.containsKey(em_key)) continue;
                                    BigDecimal calc = smrValues.get(em_key).multiply(koeff.getValues().get("PZ").subtract(new BigDecimal(1)));
                                    levSum.put(em_key, levSum.containsKey(em_key) ? levSum.get(em_key).add(calc) : calc);
                                }
                                break;

                            } else if (key.equals("EM")) {

                                List<String> em_keys = Arrays.asList("ZM", "EMR");
                                for (String em_key : em_keys) {
                                    if (!smrValues.containsKey(em_key)) continue;
                                    BigDecimal calc = smrValues.get(em_key).multiply(koeff.getValues().get("EM").subtract(new BigDecimal(1)));
                                    levSum.put(em_key, levSum.containsKey(em_key) ? levSum.get(em_key).add(calc) : calc);

                                }

                            } else {

                                BigDecimal calc = smrValues.get(key).multiply(koeff.getValues().get(key).subtract(new BigDecimal(1)));
                                levSum.put(key, levSum.containsKey(key) ? levSum.get(key).add(calc) : calc);
                            }

                        }

                    }

                }
                //endregion

            }

            for (String key : levSum.keySet()) {
                smrValues.put(key, smrValues.get(key).add(levSum.get(key)));
            }

        }


        smrValues.put("EM",
                (smrValues.containsKey("ZM")? smrValues.get("ZM") : smrValues.get("0"))
                        .add(smrValues.containsKey("EMR") ? smrValues.get("EMR") : new BigDecimal(0)));
    }

}

class RegionInfo {

    private String regionName, zone84Name, zone01Name;
    private Integer regionId, zone84Id, zone01Id;

    public String getRegionName() {
        return regionName;
    }

    public void setRegionName(String regionName) {
        this.regionName = regionName;
    }

    public String getZone84Name() {
        return zone84Name;
    }

    public void setZone84Name(String zone84Name) {
        this.zone84Name = zone84Name;
    }

    public String getZone01Name() {
        return zone01Name;
    }

    public void setZone01Name(String zone01Name) {
        this.zone01Name = zone01Name;
    }

    public Integer getRegionId() {
        return regionId;
    }

    public void setRegionId(Integer regionId) {
        this.regionId = regionId;
    }

    public Integer getZone84Id() {
        return zone84Id;
    }

    public void setZone84Id(Integer zone84Id) {
        this.zone84Id = zone84Id;
    }

    public Integer getZone01Id() {
        return zone01Id;
    }

    public void setZone01Id(Integer zone01Id) {
        this.zone01Id = zone01Id;
    }

    public RegionInfo() {

    }


}

class Parameters {

    private Boolean roundSum, addZatr, globalNR, globalSP;
    private String basePrices, baseCalcVrs;
    private int tzDigits;

    public String getBasePrices() {
        return basePrices;
    }

    public void setBasePrices(String basePrices) {
        this.basePrices = basePrices;
    }

    public String getBaseCalcVrs() {
        return baseCalcVrs;
    }

    public void setBaseCalcVrs(String baseCalcVrs) {
        this.baseCalcVrs = baseCalcVrs;
    }

    public Boolean getRoundSum() {
        return roundSum;
    }

    public void setRoundSum(Boolean roundSum) {
        this.roundSum = roundSum;
    }

    public Boolean getAddZatr() {
        return addZatr;
    }

    public void setAddZatr(Boolean addZatr) {
        this.addZatr = addZatr;
    }

    public Boolean getGlobalNR() {
        return globalNR;
    }

    public void setGlobalNR(Boolean globalNR) {
        this.globalNR = globalNR;
    }

    public Boolean getGlobalSP() {
        return globalSP;
    }

    public void setGlobalSP(Boolean globalSP) {
        this.globalSP = globalSP;
    }

    public Parameters() {


    }

    public int getTzDigits() {
        return tzDigits;
    }

    public void setTzDigits(int tzDigits) {
        this.tzDigits = tzDigits;
    }

}

class Koefficients {

    private ArrayList<Integer> levels;
    private HashMap<Integer, ArrayList<Koeff>> koeffs;

    public Koefficients () {

        levels = new ArrayList<>();
        koeffs = new HashMap<>();

    }

    public void sortLevels() {

        Collections.sort(levels, new Comparator<Integer>() {
            public int compare(Integer o1, Integer o2) {
                return o1.compareTo(o2);
            }
        });

    }

    public ArrayList<Integer> getLevels() {
        return levels;
    }

    public void setLevels(ArrayList<Integer> levels) {
        this.levels = levels;
    }

    public HashMap<Integer, ArrayList<Koeff>> getKoeffs() {
        return koeffs;
    }

    public void setKoeffs(HashMap<Integer, ArrayList<Koeff>> koeffs) {
        this.koeffs = koeffs;
    }

}

class Koeff {

    private Boolean allVidRabs, allChapters;
    private ArrayList<String> vrsLink, chaptersLink;

    public HashMap<String, BigDecimal> getValues() {
        return values;
    }

    HashMap<String, BigDecimal> values;

    public Koeff() {

        allVidRabs = true;
        allChapters = true;
        values = new HashMap<>();

    }

    public Boolean getAllVidRabs() {
        return allVidRabs;
    }

    public void setAllVidRabs(Boolean allVidRabs) {
        this.allVidRabs = allVidRabs;
        if (!allVidRabs) vrsLink = new ArrayList<>();
    }

    public Boolean getAllChapters() {
        return allChapters;
    }

    public void setAllChapters(Boolean allChapters) {
        this.allChapters = allChapters;
        if (!allChapters) chaptersLink = new ArrayList<>();
    }

    public ArrayList<String> getVrsLink() {
        return vrsLink;
    }

    public void setVrsLink(ArrayList<String> vrsLink) {
        this.vrsLink = vrsLink;
    }

    public ArrayList<String> getChaptersLink() {
        return chaptersLink;
    }

    public void setChaptersLink(ArrayList<String> chaptersLink) {
        this.chaptersLink = chaptersLink;
    }

}